// Copyright 2013 The ChromeOS IME Authors. All Rights Reserved.
// limitations under the License.
// See the License for the specific language governing permissions and
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// distributed under the License is distributed on an "AS-IS" BASIS,
// Unless required by applicable law or agreed to in writing, software
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// You may obtain a copy of the License at
// you may not use this file except in compliance with the License.
// Licensed under the Apache License, Version 2.0 (the "License");
//

/**
 * @fileoverview Definition of compose text input.
 */

goog.provide('i18n.input.chrome.vk.ComposeTextInput');

goog.require('i18n.input.chrome.vk.DeferredApi');
goog.require('i18n.input.chrome.vk.TextInput');



/**
 * The compose-style text input, which will show composition line when typing.
 *
 * @param {InputContext} context The input box context.
 * @param {!i18n.input.chrome.vk.Model} model The vk model instance.
 * @constructor
 * @extends {i18n.input.chrome.vk.TextInput}
 */
i18n.input.chrome.vk.ComposeTextInput = function(context, model) {
  goog.base(this, context);

  /**
   * The vk model.
   *
   * @type {!i18n.input.chrome.vk.Model}
   * @private
   */
  this.model_ = model;

  // These char sets were generated by a perl script as like below.
  // Note: regular expression \p{mn} can match non-spacing marks, while \p{m}
  // can match all marks including non-spacing, spacing & enclosing.
  //
  //  @non_spacing_codes = ();
  //  for ($i=0; $i<0xFFFF; $i++) {
  //    if (chr($i) =~ /[\p{mn}]/) {
  //      push(@non_spacing_codes, $i);
  //    }
  //  }
  //  $str = "";
  //  $begin = 0;
  //  $end = 0;
  //  foreach (@other_mark_codes) {
  //    $code = $_;
  //    if ($end + 1 eq $code) {
  //      $end++;
  //    } else {
  //      if ($begin gt 0) {
  //        $str .= sprintf("\\u%04X", $begin);
  //      }
  //      if ($end gt $begin) {
  //        if ($end gt $begin + 1) {
  //          $str .= "-";
  //        }
  //        $str .= sprintf("\\u%04X", $end);
  //      }
  //      $begin = $code;
  //      $end = $code;
  //    }
  //  }
  //  print $str."\n";
  var nonSpacingCharSet = '\u0300-\u036F\u0483-\u0487\u0591-\u05BD\u05BF' +
      '\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-' +
      '\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6' +
      '-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-' +
      '\u082D\u0859-\u085B\u08E4-\u08FE\u0900-\u0902\u093A\u093C\u0941-' +
      '\u0948\u094D\u0951-\u0957\u0962\u0963\u0981\u09BC\u09C1-\u09C4\u09CD' +
      '\u09E2\u09E3\u0A01\u0A02\u0A3C\u0A41\u0A42\u0A47\u0A48\u0A4B-\u0A4D' +
      '\u0A51\u0A70\u0A71\u0A75\u0A81\u0A82\u0ABC\u0AC1-\u0AC5\u0AC7\u0AC8' +
      '\u0ACD\u0AE2\u0AE3\u0B01\u0B3C\u0B3F\u0B41-\u0B44\u0B4D\u0B56\u0B62' +
      '\u0B63\u0B82\u0BC0\u0BCD\u0C3E-\u0C40\u0C46-\u0C48\u0C4A-\u0C4D\u0C55' +
      '\u0C56\u0C62\u0C63\u0CBC\u0CBF\u0CC6\u0CCC\u0CCD\u0CE2\u0CE3\u0D41-' +
      '\u0D44\u0D4D\u0D62\u0D63\u0DCA\u0DD2-\u0DD4\u0DD6\u0E31\u0E34-\u0E3A' +
      '\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19' +
      '\u0F35\u0F37\u0F39\u0F71-\u0F7E\u0F80-\u0F84\u0F86\u0F87\u0F8D-\u0F97' +
      '\u0F99-\u0FBC\u0FC6\u102D-\u1030\u1032-\u1037\u1039\u103A\u103D\u103E' +
      '\u1058\u1059\u105E-\u1060\u1071-\u1074\u1082\u1085\u1086\u108D\u109D' +
      '\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4' +
      '\u17B5\u17B7-\u17BD\u17C6\u17C9-\u17D3\u17DD\u180B-\u180D\u18A9\u1920' +
      '-\u1922\u1927\u1928\u1932\u1939-\u193B\u1A17\u1A18\u1A56\u1A58-\u1A5E' +
      '\u1A60\u1A62\u1A65-\u1A6C\u1A73-\u1A7C\u1A7F\u1B00-\u1B03\u1B34\u1B36' +
      '-\u1B3A\u1B3C\u1B42\u1B6B-\u1B73\u1B80\u1B81\u1BA2-\u1BA5\u1BA8\u1BA9' +
      '\u1BAB\u1BE6\u1BE8\u1BE9\u1BED\u1BEF-\u1BF1\u1C2C-\u1C33\u1C36\u1C37' +
      '\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8\u1CED\u1CF4\u1DC0-\u1DE6' +
      '\u1DFC-\u1DFF\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2CEF-\u2CF1\u2D7F' +
      '\u2DE0-\u2DFF\u302A-\u302D\u3099\u309A\uA66F\uA674-\uA67D\uA69F' +
      '\uA6F0\uA6F1\uA802\uA806\uA80B\uA825\uA826\uA8C4\uA8E0-\uA8F1\uA926-' +
      '\uA92D\uA947-\uA951\uA980-\uA982\uA9B3\uA9B6-\uA9B9\uA9BC\uAA29-' +
      '\uAA2E\uAA31\uAA32\uAA35\uAA36\uAA43\uAA4C\uAAB0\uAAB2-\uAAB4\uAAB7' +
      '\uAAB8\uAABE\uAABF\uAAC1\uAAEC\uAAED\uAAF6\uABE5\uABE8\uABED\uFB1E' +
      '\uFE00-\uFE0F';

  var otherMarkCharSet = '\u0488\u0489\u0903\u093B\u093E-\u0940\u0949-\u094C' +
      '\u094E\u094F\u0982\u0983\u09BE-\u09C0\u09C7\u09C8\u09CB\u09CC\u09D7' +
      '\u0A03\u0A3E-\u0A40\u0A83\u0ABE-\u0AC0\u0AC9\u0ACB\u0ACC\u0B02\u0B03' +
      '\u0B3E\u0B40\u0B47\u0B48\u0B4B\u0B4C\u0B57\u0BBE\u0BBF\u0BC1\u0BC2' +
      '\u0BC6-\u0BC8\u0BCA-\u0BCC\u0BD7\u0C01-\u0C03\u0C41-\u0C44\u0C82' +
      '\u0C83\u0CBE\u0CC0-\u0CC4\u0CC7\u0CC8\u0CCA\u0CCB\u0CD5\u0CD6\u0D02' +
      '\u0D03\u0D3E-\u0D40\u0D46-\u0D48\u0D4A-\u0D4C\u0D57\u0D82\u0D83\u0DCF' +
      '-\u0DD1\u0DD8-\u0DDF\u0DF2\u0DF3\u0F3E\u0F3F\u0F7F\u102B\u102C\u1031' +
      '\u1038\u103B\u103C\u1056\u1057\u1062-\u1064\u1067-\u106D\u1083\u1084' +
      '\u1087-\u108C\u108F\u109A-\u109C\u17B6\u17BE-\u17C5\u17C7\u17C8\u1923' +
      '-\u1926\u1929-\u192B\u1930\u1931\u1933-\u1938\u19B0-\u19C0\u19C8' +
      '\u19C9\u1A19-\u1A1B\u1A55\u1A57\u1A61\u1A63\u1A64\u1A6D-\u1A72\u1B04' +
      '\u1B35\u1B3B\u1B3D-\u1B41\u1B43\u1B44\u1B82\u1BA1\u1BA6\u1BA7\u1BAA' +
      '\u1BAC\u1BAD\u1BE7\u1BEA-\u1BEC\u1BEE\u1BF2\u1BF3\u1C24-\u1C2B\u1C34' +
      '\u1C35\u1CE1\u1CF2\u1CF3\u20DD-\u20E0\u20E2-\u20E4\u302E\u302F\uA670' +
      '-\uA672\uA823\uA824\uA827\uA880\uA881\uA8B4-\uA8C3\uA952\uA953\uA983' +
      '\uA9B4\uA9B5\uA9BA\uA9BB\uA9BD-\uA9C0\uAA2F\uAA30\uAA33\uAA34\uAA4D' +
      '\uAA7B\uAAEB\uAAEE\uAAEF\uAAF5\uABE3\uABE4\uABE6\uABE7\uABE9\uABEA';

  /**
   * The regular expression to test combining mark chars.
   * It needs to match:
   * 1) ANAN, e.g. in Hindi, 'ste' -> 'स्ते' -> "स" + "्" + "त" + "े".
   * 2) ANA, e.g. in Vietnamese Telex, 'aof' -> 'ào' -> "a" + "̀" + "o".
   * 3) ANAS, e.g. in Hindi, 'ndi' -> 'न्दि' -> "न" + "्" + "द" + "ि".
   *
   * @type {!RegExp}
   * @private
   */
  this.reMarks_ = new RegExp('((?:A(?:NA?)+)|(?:(?:AN*)+S))$'.
      replace(/N/g, '[' + nonSpacingCharSet + ']').
      replace(/S/g, '[' + otherMarkCharSet + ']').
      replace(/A/g, '[^' + nonSpacingCharSet + otherMarkCharSet + ']'));
};
goog.inherits(i18n.input.chrome.vk.ComposeTextInput,
    i18n.input.chrome.vk.TextInput);


/**
 * Sets the composing text.
 *
 * @param {string} text The composing text.
 * @private
 */
i18n.input.chrome.vk.ComposeTextInput.prototype.setComposingText_ = function(
    text) {
  i18n.input.chrome.vk.DeferredApi.setComposition(
      this.context.contextID, text, text.length);
  this.textBeforeCursor = text;
};


/**
 * Commits text.
 *
 * @param {string} text The text to be committed.
 * @private
 */
i18n.input.chrome.vk.ComposeTextInput.prototype.commitText_ = function(
    text) {
  i18n.input.chrome.vk.DeferredApi.commitText(this.context.contextID, text);
  this.textBeforeCursor = '';
};


/** @override */
i18n.input.chrome.vk.ComposeTextInput.prototype.commitText = function(
    text, back) {
  var onStage = !!this.textBeforeCursor;
  if (!onStage && (back == 1 && !text || back == 0 && text == ' ')) {
    return false;
  }
  if (onStage && text == ' ' && back == 0) {
    this.commitText_(this.textBeforeCursor + ' ');
    this.textBeforeCursor = '';
    return true;
  }
  var len = this.textBeforeCursor.length;
  text = this.textBeforeCursor.slice(0, len - back) + text;
  if (!text) {
    this.setComposingText_(text);
    return true;
  }
  var pos = this.model_.predictHistory();
  if (pos > 0) {
    var text2 = text.slice(0, pos + 1);
    var matches = this.reMarks_.exec(text2);
    if (matches) {
      pos = text2.length - matches[0].length;
    }
  }
  if (pos < 0) {
    this.commitText_(text);
    this.model_.clearHistory();
  } else if (pos == 0) {
    this.setComposingText_(text);
  } else {
    i18n.input.chrome.vk.DeferredApi.commitText(
        this.context.contextID, text.slice(0, pos));
    this.setComposingText_(text.slice(pos));
    // Prunes history so that this.textBeforeCursor is consistent with the
    // model's internal history text.
    this.model_.pruneHistory(pos);
  }
  return true;
};


/** @override */
i18n.input.chrome.vk.ComposeTextInput.prototype.reset = function() {
  if (this.textBeforeCursor) {
    i18n.input.chrome.vk.DeferredApi.commitText(
        this.context.contextID, this.textBeforeCursor);
    this.textBeforeCursor = '';
  }
};
